﻿using System;
using System.IO;

namespace BLL.Core
{
    public class CoreService : IPackage, ICoreService, IServiceProvider, IServiceContainer, IDisposable
    {
        private static CoreService _instance;
        private static object _lock = new object();

        public static CoreService Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_lock)
                    {
                        if (_instance == null)
                            _instance = new CoreService();
                    }
                }
                return _instance;
            }
        }

        protected CoreService()
        {
            _guid = Guid.NewGuid();
        }

        private Guid _guid;

        Guid ICoreService.GetGUID()
        {
            return _guid;
        }

        private ServiceCollection<object> _services = new ServiceCollection<object>();
        private bool zombie = false;
        private ServiceProvider _provider;

        protected virtual object GetService(Type serviceType)
        {
            if (zombie)
            {
                return null;
            }
            if (serviceType == null)
            {
                throw new ArgumentNullException("serviceType");
            }
            if (serviceType.IsEquivalentTo(typeof(IServiceContainer)) 
                || serviceType.IsEquivalentTo(typeof(CoreService)) 
                || serviceType.IsEquivalentTo(GetType()))
            {
                return this;
            }
            object obj = null;
            if (_services != null)
            {
                lock (_services)
                {
                    if (_services.Count > 0)
                    {
                        if (_services.ContainsKey(serviceType))
                        {
                            obj = _services[serviceType];
                        }

                        if (obj is ProfferedService)
                        {
                            obj = ((ProfferedService)obj).Instance;
                        }

                        if (obj is ServiceCreatorCallback)
                        {
                            _services[serviceType] = null;
                        }
                    }
                }

                bool flag = false;
                if (obj is ServiceCreatorCallback)
                {
                    flag = true;
                    obj = ((ServiceCreatorCallback)obj)(this, serviceType);
                    lock (_services)
                    {
                        _services[serviceType] = obj;
                    }
                }

                //if (flag && obj == null)
                //{
                //    string text = "An object was not returned from a service creator callback for the registered type of " + serviceType.Name + ".  This may mean that it failed a type equivalence comparison.  To compare type objects you must use Type.IsEquivalentTo(Type).  Do not use .Equals or the == operator.";
                //    MessageBox.Show(text);
                //}
            }

            if (obj == null && _provider != null && (_services == null || _services.Count == 0 || !_services.ContainsKey(serviceType)))
            {
                obj = _provider.GetService(serviceType);
            }

            return obj;
        }

        object IServiceProvider.GetService(Type serviceType)
        {
            return this.GetService(serviceType);
        }

        #region IServiceContainer接口实现


        void IServiceContainer.AddService(Type serviceType, object serviceInstance)
        {
            ((IServiceContainer)this).AddService(serviceType, serviceInstance, promote: false);
        }

        void IServiceContainer.AddService(Type serviceType, object serviceInstance, bool promote)
        {
            if (serviceType == null)
            {
                throw new ArgumentNullException("serviceType");
            }

            if (serviceInstance == null)
            {
                throw new ArgumentNullException("serviceInstance");
            }

            lock (_services)
            {
                if (_services.ContainsKey(serviceType))
                {
                    throw new InvalidOperationException(serviceType.FullName);
                }
                if (promote)
                {
                    ProfferedService profferedService = new ProfferedService();
                    _services[serviceType] = profferedService;
                    profferedService.Instance = serviceInstance;
                    if (_provider != null)
                    {
                        IProfferService profferService = (IProfferService)GetService(typeof(SProfferService));
                        if (profferService != null)
                        {
                            Guid rguidService = serviceType.GUID;
                            profferService.ProfferService(ref rguidService, this, out var pdwCookie);
                            profferedService.Cookie = pdwCookie;
                        }
                    }
                }
                else
                {
                    _services[serviceType] = serviceInstance;
                }
            }
        }

        void IServiceContainer.AddService(Type serviceType, ServiceCreatorCallback callback)
        {
            ((IServiceContainer)this).AddService(serviceType, callback, promote: false);
        }

        void IServiceContainer.AddService(Type serviceType, ServiceCreatorCallback callback, bool promote)
        {
            if (serviceType == null)
            {
                throw new ArgumentNullException("serviceType");
            }

            if (callback == null)
            {
                throw new ArgumentNullException("callback");
            }

            ((IServiceContainer)this).AddService(serviceType, (object)callback, promote);
        }

        void IServiceContainer.RemoveService(Type serviceType)
        {
            ((IServiceContainer)this).RemoveService(serviceType, promote: false);
        }

        void IServiceContainer.RemoveService(Type serviceType, bool promote)
        {
            if (serviceType == null)
            {
                throw new ArgumentNullException("serviceType");
            }

            if (_services == null)
            {
                return;
            }

            lock (_services)
            {
                if (_services.Count <= 0)
                {
                    return;
                }

                object obj = null;
                if (_services.ContainsKey(serviceType))
                {
                    obj = _services[serviceType];
                }

                if (!_services.Remove(serviceType))
                {
                    return;
                }

                try
                {
                    ProfferedService profferedService = obj as ProfferedService;
                    if (profferedService == null)
                    {
                        return;
                    }
                    obj = profferedService.Instance;
                    if (profferedService.Cookie != 0)
                    {
                        IProfferService profferService = (IProfferService)GetService(typeof(SProfferService));
                        if (profferService != null)
                        {
                            profferService.RevokeService(profferedService.Cookie);
                        }
                        profferedService.Cookie = 0u;
                    }
                }
                finally
                {
                    if (obj is IDisposable)
                    {
                        ((IDisposable)obj).Dispose();
                    }
                }
            }
        }

        void IPackage.Close()
        {
            throw new NotImplementedException();
        }

        #endregion

        protected sealed class ProfferedService
        {
            public object Instance;

            public uint Cookie;
        }

        #region IDispose

        private bool disposedValue;
        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)
                }
                if (_services != null && _services.Count > 0)
                {
                    try
                    {
                        IProfferService profferService = (IProfferService)GetService(typeof(SProfferService));
                        ServiceCollection<object> services = _services;
                        _services = null;
                        foreach (object value in services.Values)
                        {
                            object obj = value;
                            ProfferedService profferedService = obj as ProfferedService;
                            try
                            {
                                if (profferedService == null)
                                {
                                    continue;
                                }
                                obj = profferedService.Instance;
                                if (profferedService.Cookie != 0 && profferService != null)
                                {
                                    int hr = profferService.RevokeService(profferedService.Cookie);
                                    //if (Microsoft.VisualStudio.NativeMethods.Failed(hr))
                                    //{
                                    //    Trace.WriteLine(string.Format(CultureInfo.CurrentUICulture, "Failed to unregister service {0}", obj.GetType().FullName));
                                    //}
                                }
                            }
                            finally
                            {
                                if (obj is IDisposable)
                                {
                                    ((IDisposable)obj).Dispose();
                                }
                            }
                        }
                    }
                    catch (Exception)
                    {
                    }
                }

                if (_provider != null)
                {
                    try
                    {
                        _provider.Dispose();
                    }
                    catch (Exception)
                    {
                    }
                    _provider = null;
                }
                // TODO: 释放未托管的资源(未托管的对象)并重写终结器
                // TODO: 将大型字段设置为 null
                disposedValue = true;
            }
        }

        // // TODO: 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器
        // ~CoreService()
        // {
        //     // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
        //     Dispose(disposing: false);
        // }

        public void Dispose()
        {
            // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }

        #endregion
    }

    public interface IPackage
    {
        void Close();
    }

    public interface IPersistSolutionOpts
    {
        int SaveUserOptions(ISolutionPersistence pPersistence);

        int LoadUserOptions(ISolutionPersistence pPersistence, uint grfLoadOpts);

        int WriteUserOptions(Stream pOptionsStream, string pszKey);

        int ReadUserOptions(Stream pOptionsStream, string pszKey);
    }

    public interface ISolutionPersistence
    {
        //int SavePackageSolutionProps(int fPreLoad, IVsHierarchy pHierarchy,IVsPersistSolutionProps ppSP, string pszKey);
        int SavePackageSolutionProps(int fPreLoad, string pszKey);

        int SavePackageUserOpts(IPersistSolutionOpts pPSO, string pszKey);

        int LoadPackageUserOpts(IPersistSolutionOpts pPSO, string pszKey);
    }
}